﻿using MahApps.Metro.Controls;
using MahApps.Metro.Controls.Dialogs;
using Microsoft.Win32;
using Microsoft.WindowsAPICodePack.Dialogs;
using MiniExcelLibs;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;

namespace ORMTool
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : MetroWindow, INotifyPropertyChanged
    {
        private string excelFilePath;
        private string classFilePath;
        private string settingFilePath;
        private List<DBTableInfo> tables;
        private MetroDialogSettings mySettings = new MetroDialogSettings()
        {
            AffirmativeButtonText = "确定",
            NegativeButtonText = "取消",
            FirstAuxiliaryButtonText = "取消",
            DialogButtonFontSize = 16D
        };
        public MainWindow()
        {
            InitializeComponent();
            this.Loaded += MainWindow_Loaded;
        }
        private void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            settingFilePath = $@"{Environment.CurrentDirectory}\setting.xml";
            if (!File.Exists(settingFilePath))
            {
                Setting = new TemplateSetting();
                XMLService.Save<TemplateSetting>(Setting, settingFilePath);
            }
            else
                Setting = XMLService.Get<TemplateSetting>(settingFilePath);
        }
        #region 绑定数据
        private Visibility _IsBusy = Visibility.Collapsed;
        public Visibility IsBusy
        {
            get { return _IsBusy; }
            set
            {
                _IsBusy = value;
                OnPropertyChanged();
            }
        }
        private bool _IsCompleted = false;
        public bool IsCompleted
        {
            get { return _IsCompleted; }
            set
            {
                _IsCompleted = value;
                NotifyOfPropertyChange(() => IsCompleted);
            }
        }
        private TemplateSetting _Setting;
        public TemplateSetting Setting
        {
            get { return _Setting; }
            set
            {
                _Setting = value;
                NotifyOfPropertyChange(() => Setting);
            }
        }
        private string _Message;
        public string Message
        {
            get { return _Message; }
            set
            {
                _Message = value;
                NotifyOfPropertyChange(() => Message);
            }
        }
        private ObservableCollection<DBFieldInfo> _FieldCollection;

        public ObservableCollection<DBFieldInfo> FieldCollection
        {
            get
            {
                return _FieldCollection;
            }
            set
            {
                _FieldCollection = value;
                NotifyOfPropertyChange(() => FieldCollection);
            }
        }
        private ObservableCollection<string> _SheetNames;
        public ObservableCollection<string> SheetNames
        {
            get
            {
                return _SheetNames;
            }
            set
            {
                _SheetNames = value;
                NotifyOfPropertyChange(() => SheetNames);
            }
        }
        private string _SelectedSheet;
        public string SelectedSheet
        {
            get { return _SelectedSheet; }
            set
            {
                if (_SelectedSheet != value)
                {
                    if (FieldCollection != null)
                    {
                        //保存当前数据
                        int index1 = tables.FindIndex(p => p.TableName == _SelectedSheet);
                        if (index1 > -1)
                            tables[index1].DBFieldInfos = FieldCollection.ToList();
                    }
                    _SelectedSheet = value;
                    int index = tables.FindIndex(p => p.TableName == _SelectedSheet);
                    if (index > -1)
                        FieldCollection = new ObservableCollection<DBFieldInfo>(tables[index].DBFieldInfos);
                    else
                        FieldCollection = new ObservableCollection<DBFieldInfo>();
                    NotifyOfPropertyChange(() => SelectedSheet);
                }
            }
        }
        #endregion
        #region 属性更新通知
        public event PropertyChangedEventHandler PropertyChanged;
        /// <summary>
        /// 属性更新通知
        /// </summary>
        /// <param name = "propertyName">属性</param>
        public virtual void NotifyOfPropertyChange([System.Runtime.CompilerServices.CallerMemberName] string propertyName = null)
        {
            OnPropertyChanged(propertyName);
        }
        /// <summary>
        /// 属性更新通知
        /// </summary>
        /// <typeparam name = "TProperty">属性类型</typeparam>
        /// <param name = "property">属性表达式</param>
        public void NotifyOfPropertyChange<TProperty>(Expression<Func<TProperty>> property)
        {
            NotifyOfPropertyChange(property.GetMemberInfo().Name);
        }
        /// <summary>
        /// 属性更新通知
        /// </summary>
        /// <param name="propertyName">属性</param>
        public void OnPropertyChanged(string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
        #endregion
        #region 设计文件
        private void Browse_Click(object sender, RoutedEventArgs e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog();
            //设置文件的类型
            openFileDialog.DefaultExt = ".xlsx";
            openFileDialog.Filter = "Excel文件|*.xlsx|Excel97-2003文件|*.xls";
            //调用ShowDialog()方法显示该对话框,该方法的返回值代表用户是否点击了确定按钮
            if (openFileDialog.ShowDialog() == true)
            {
                IsBusy = Visibility.Visible;
                IsCompleted = false;
                Task task = Task.Factory.StartNew(() =>
                {
                    excelFilePath = openFileDialog.FileName;
                    var columns = MiniExcel.GetColumns(excelFilePath, useHeaderRow: true);
                    SheetNames = new ObservableCollection<string>(MiniExcel.GetSheetNames(excelFilePath));
                    //读取全部Sheet中的数据
                    tables = new List<DBTableInfo>();
                    for (int i = 0; i < SheetNames.Count; i++)
                    {
                        string sheetName = SheetNames[i];
                        var rows = MiniExcel.Query<DBFieldInfo>(excelFilePath, sheetName: sheetName);
                        tables.Add(new DBTableInfo
                        {
                            TableName = SheetNames[i],
                            DBFieldInfos = rows.ToList()
                        });
                    }
                    if (SheetNames.Count > 0)
                        SelectedSheet = SheetNames[0];
                }).ContinueWith(t =>
                {
                    IsBusy = Visibility.Collapsed;
                    IsCompleted = true;
                });
            }
        }
        #endregion
        #region 生成实体
        private void OpenFolder_Click(object sender, RoutedEventArgs e)
        {
            var dialog = new CommonOpenFileDialog();
            dialog.IsFolderPicker = true;
            if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
            {
                classFilePath = dialog.FileName;
            }

        }
        private void GenerateClass_Click(object sender, RoutedEventArgs e)
        {
            if(tables==null ||tables.Count==0)
            {
                this.ShowMessageAsync("提示", "请选择设计数据。",
                    MessageDialogStyle.Affirmative, mySettings);
                return;
            }
            if (string.IsNullOrEmpty(classFilePath))
            {
                this.ShowMessageAsync("提示", "请选择保存路径。",
                    MessageDialogStyle.Affirmative, mySettings);
                return;
            }
            if (!Directory.Exists(classFilePath))
            {
                this.ShowMessageAsync("提示", "请选择正确的保存路径。",
                    MessageDialogStyle.Affirmative, mySettings);
                return;
            }
            if (FieldCollection != null)
            {
                //保存当前数据
                int index = tables.FindIndex(p => p.TableName == _SelectedSheet);
                if (index > -1)
                    tables[index].DBFieldInfos = FieldCollection.ToList();
            }
            IsBusy = Visibility.Visible;
            IsCompleted = false;
            Message = "";
            Task task1 = Task.Factory.StartNew(() =>
            {
                for (int i = 0; i < tables.Count; i += 2)
                {
                    string sheetName = tables[i].TableName;
                    var rows = tables[i].DBFieldInfos;
                    string csFilePath = classFilePath + $@"\{sheetName}.cs";
                    Message += $"正在处理：{sheetName}......{Environment.NewLine}";
                    GetCode(csFilePath, sheetName, rows);
                }
            });
            Task task2 = Task.Factory.StartNew(() =>
            {
                for (int i = 1; i < tables.Count; i += 2)
                {
                    string sheetName = tables[i].TableName;
                    var rows = tables[i].DBFieldInfos;
                    string csFilePath = classFilePath + $@"\{sheetName}.cs";
                    Message += $"正在处理：{sheetName}......{Environment.NewLine}";
                    GetCode(csFilePath, sheetName, rows);
                }
            });
            Task.Factory.ContinueWhenAll(new[] { task1, task2 }, tasks =>
            {
                Message += $"处理完成！";
                IsBusy = Visibility.Collapsed;
                IsCompleted = true;

            });
        }
        private void GetCode(string filePath, string tableName, List<DBFieldInfo> dBFieldInfos)
        {
            StringBuilder codeString = new StringBuilder();
            if (!string.IsNullOrEmpty(Setting.Namespace))
            {
                codeString.Append($"{Setting.Namespace}{Environment.NewLine}");
            }
            string interfaceString = Setting.INotifyChecked ? ":INotifyPropertyChanged" : "";
            interfaceString += string.IsNullOrEmpty(interfaceString) ? ":" : "," + (Setting.IEquatableChecked ? $"IEquatable<{tableName}>" : "");
            interfaceString += string.IsNullOrEmpty(interfaceString) ? ":" : "," + Setting.ParentClass;
            interfaceString = interfaceString.TrimEnd(',');
            string inotifyString = (Setting.INotifyChecked ? Environment.NewLine : "") +
                (Setting.INotifyChecked ? "                 OnPropertyChanged(\"{0}\")" : "");
            codeString.Append($"using System.ComponentModel;{Environment.NewLine}");
            codeString.Append($"using System.ComponentModel.DataAnnotations;{Environment.NewLine}");
            codeString.Append($@"using System.ComponentModel.DataAnnotations.Schema;
namespace Entitys
{{
    {(string.IsNullOrEmpty(Setting.CustomAttr) ? "" : Setting.CustomAttr)}
    public class {tableName} {interfaceString}
    {{
        public {tableName}()
        {{
        }}");
            var pks = dBFieldInfos.Where(p => p.PK == 1);
            string hashString = "";
            foreach (DBFieldInfo pk in pks)
            {
                hashString += $"this.{pk.Name}.ToString()+";
            }
            hashString = hashString.TrimEnd('+');
            int pkCount = pks.Count();
            hashString = pkCount == 0 ? $"this.{dBFieldInfos.FirstOrDefault()?.Name}" : hashString;
            int pkIndex = 1;
            foreach (DBFieldInfo d in dBFieldInfos)
            {
                string csType = ChangeToCSharpType(d.Type);
                string defaultValue = string.IsNullOrEmpty(d.DefaultValue) ? "" : $"={d.DefaultValue}";
                codeString.Append($@"
        private {csType} _{d.Name}{defaultValue};");
                string atrString = null;
                string notmap = null;
                if (d.Create == 1)
                    notmap = $"{Environment.NewLine}        [NotMapped]";
                if (csType == "string" || d.Nullable == 0 || d.PK == 1)
                {
                    //[Key, Column(Order = 2), MaxLength(50), Required]
                    atrString =
                    (d.PK == 1 ? "Key, Column(Order = " + (pkIndex++) + ")," : "") +
                    (csType == "string" ? "MaxLength(" + d.Length + ")," : "") +
                    (d.Nullable == 0 ? "Required" : "");
                    atrString = "[" + atrString.TrimEnd(',') + "]";
                }
                codeString.Append($@"{(notmap == null ? "" : notmap)}
        {(atrString == null ? "" : atrString)}
        public {csType} {d.Name}
        {{
            get=> _{d.Name};
            set
            {{
                _{d.Name}=value;{(Setting.INotifyChecked ? string.Format(inotifyString, d.Name) : "")}
            }}
        }}");
            }
            if (Setting.IEquatableChecked)
            {
                codeString.Append($@"
        public override bool Equals(object obj)
        {{
            //覆盖基类中的Equals方法
            if (obj == null)
                return base.Equals(obj);
            return Equals(obj as {tableName});
        }}
        public bool Equals({tableName} other)
        {{
            if (Object.ReferenceEquals(this, other)) return true;
            if (this == null || other == null)
                return false;
            else
                return this.GetHashCode() == other.GetHashCode();
        }}
        {(pkCount == 0 ? "//未找到主键，GetHashCode错误" : "")}
        public override int GetHashCode()
        {{
            return ({hashString}).GetHashCode();
        }}");
            }

            if (Setting.INotifyChecked)
            {
                codeString.Append($@"
        #region 属性更新通知
        public event PropertyChangedEventHandler PropertyChanged;
        /// <summary>
        /// 属性更新通知
        /// </summary>
        /// <param name = ""propertyName"">属性</param>
        public void OnPropertyChanged(string propertyName = null)
        {{
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }}
        #endregion");
            }
            codeString.Append($@"
    }}
}}");
            
            using (StreamWriter sw = new StreamWriter(filePath))
            {
                sw.Write(codeString.ToString());
            }
        }
        private string ChangeToCSharpType(string type)
        {
            string reval = string.Empty;
            switch (type.ToLower())
            {
                case "int":
                    reval = "int";
                    break;
                case "text":
                    reval = "string";
                    break;
                case "bigint":
                    reval = "Int64";
                    break;
                case "binary":
                    reval = "byte[]";
                    break;
                case "bit":      // 0为false
                    reval = "bool";
                    break;
                case "char":
                    reval = "string";
                    break;
                case "datetime":
                    reval = "Datetime";
                    break;
                case "decimal":
                    reval = "decimal";
                    break;
                case "float":
                    reval = "double";
                    break;
                case "image":
                    reval = "byte[]";
                    break;
                case "money":
                    reval = "decimal";
                    break;
                case "nchar":
                    reval = "string";
                    break;
                case "ntext":
                    reval = "string";
                    break;
                case "numeric":
                    reval = "decimal";
                    break;
                case "nvarchar":
                    reval = "string";
                    break;
                case "real":
                    reval = "float";
                    break;
                case "smalldatetime":
                    reval = "DateTime";
                    break;
                case "smallint":
                    reval = "int";
                    break;
                case "smallmoney":
                    reval = "decimal";
                    break;
                case "timestamp":
                    reval = "Datetime";
                    break;
                case "tinyint":
                    reval = "byte";
                    break;
                case "uniqueidentifier":
                    reval = "Guid";
                    break;
                case "varbinary":
                    reval = "byte[]";
                    break;
                case "varchar":
                    reval = "string";
                    break;
                case "variant":
                    reval = "object";
                    break;
                default:
                    reval = "string";
                    break;
            }
            return reval;
        }
        #endregion
        #region 模板设置
        private void SaveSetting_Click(object sender, RoutedEventArgs e)
        {
            XMLService.Save<TemplateSetting>(Setting, settingFilePath);
        }
        #endregion
        #region 其他界面补充
        private void HamburgerMenuControl_OnItemInvoked(object sender, HamburgerMenuItemInvokedEventArgs args)
        {
            if (args.IsItemOptions)
                tabControl.SelectedIndex = 2;
        }

        private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            txtMessage.ScrollToEnd();
        }
        #endregion
        #region 所需实体
        public class DBFieldInfo
        {
            public string Name { get; set; }
            public string Type { get; set; }
            public int Length { get; set; }
            public int Nullable { get; set; } = 1;
            public int PK { get; set; } = 0;
            public string DefaultValue { get; set; }
            public int Create { get; set; }
        }
        public class DBTableInfo
        {
            public string TableName { get; set; }
            public List<DBFieldInfo> DBFieldInfos { get; set; }
        }
        public class TemplateSetting : INotifyPropertyChanged
        {
            private string _Namespace;
            public string Namespace { get => _Namespace; set { _Namespace = value; OnPropertyChanged("Namespace"); } }
            private string _Modelspace;
            public string Modelspace { get => _Modelspace; set { _Modelspace = value; OnPropertyChanged("Modelspace"); } }
            private string _ParentClass;
            public string ParentClass { get => _ParentClass; set { _ParentClass = value; OnPropertyChanged("ParentClass"); } }
            private string _CustomAttr;
            public string CustomAttr { get => _CustomAttr; set { _CustomAttr = value; OnPropertyChanged("CustomAttr"); } }
            private bool _INotifyChecked = true;
            public bool INotifyChecked { get => _INotifyChecked; set { _INotifyChecked = value; OnPropertyChanged("INotifyChecked"); } }
            private bool _IEquatableChecked = false;
            public bool IEquatableChecked { get => _IEquatableChecked; set { _IEquatableChecked = value; OnPropertyChanged("IEquatableChecked"); } }
            #region 属性更新通知
            public event PropertyChangedEventHandler PropertyChanged;
            /// <summary>
            /// 属性更新通知
            /// </summary>
            /// <param name = "propertyName">属性</param>
            public void OnPropertyChanged(string propertyName = null)
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
            #endregion
        }
        #endregion
    }
}
